#include "Manager.h"

/*-----------------------------------------------------------------------------
 *  Variáveis estáticas
 *-----------------------------------------------------------------------------*/
uintArray Manager::nRows = NULL;
uintArray Manager::initRow = NULL;
uint Manager::nProcs;
uint Manager::rank;

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Manager
 *      Method:  Manager
 * Description:  Inicializa o MPI e o gerenciador
 *--------------------------------------------------------------------------------------
 */
void Manager::init()
{
    MPI::Init();

    /* Recupera o número de processos e o rank do processo atual */
    nProcs = MPI::COMM_WORLD.Get_size();
    rank = MPI::COMM_WORLD.Get_rank();

    assert(nProcs >= 2);

    nRows = new uint[nProcs];
    initRow = new uint[nProcs];
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Manager
 *      Method:  Manager
 * Description:  Finaliza o MPI
 *--------------------------------------------------------------------------------------
 */
void Manager::finalize()
{
    MPI::Finalize();

    if(nRows != NULL)
        delete[] nRows;
    if(initRow != NULL)
        delete[] initRow;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Manager
 *      Method:  distribute
 * Description:  Distribui as linhas entre os processos
 *--------------------------------------------------------------------------------------
 */
void Manager::distribute(uint nrows)
{
    /* Divisão inteira de quantas linhas cada processo fica responsável */
    uint div = nrows / (nProcs - 1);

    /* Resto devido a divisão inteira */
    uint rem = nrows - div * (nProcs - 1);

    /* Atribui a divisão inteira */
    nRows[0] = 0;
    for(uint i = 1; i < nProcs; i++)
        nRows[i] = div;

    /* Divide o resto de forma balanceada entre os primeiros processos */
    for(uint i = 1; i <= rem; i++)
        nRows[i]++;

    /* Atribui as linhas de início para cada processo */
    initRow[0] = initRow[1] = 0;
    for(uint i = 2; i < nProcs; i++)
        initRow[i] = initRow[i-1] + nRows[i-1];

    printName();
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Manager
 *      Method:  getNRows
 * Description:  Retorna a quantidade de linhas no passo step
 *--------------------------------------------------------------------------------------
 */
uint Manager::getNRows(uint step)
{
    return nRows[nextRank(step)];
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Manager
 *      Method:  getInitRow
 * Description:  Retorna a linha inicial no passo step
 *--------------------------------------------------------------------------------------
 */
uint Manager::getInitRow(uint step)
{
    return initRow[nextRank(step)];
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Manager
 *      Method:  nextRank
 * Description:  Retorna o próximo rank
 *--------------------------------------------------------------------------------------
 */
uint Manager::nextRank(uint step)
{
    int irank = rank;
    int istep = step;
    int iProcs = nProcs;

    return (irank + istep - 1) % (iProcs - 1) + 1;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Manager
 *      Method:  prevRank
 * Description:  Retorna o rank anterior
 *--------------------------------------------------------------------------------------
 */
uint Manager::prevRank(uint step)
{
    int irank = rank;
    int istep = step;
    int iProcs = nProcs;

    int rem = (irank - istep - 1) % (iProcs - 1);
    if(rem < 0)
        rem += (iProcs - 1);

    return rem + 1;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  Manager
 *      Method:  printName
 * Description:  Imprime o nome do nó atual
 *--------------------------------------------------------------------------------------
 */
void Manager::printName()
{
    char computerName[MPI::MAX_PROCESSOR_NAME];

    int nameSize;
    MPI::Get_processor_name(computerName, nameSize);

    cout << "I'm " << computerName << ", rank " << (rank + 1) << " of " << nProcs << " tasks";

    if(rank == 0)
        cout << " and i'm the master" << endl;
    else
        cout << " and i'm responsible for the row " << initRow[rank] <<
                " to the row " << (initRow[rank] + nRows[rank] - 1) << endl;

}
